package yxy.apple.http.request;

import java.nio.ByteBuffer;
import java.util.ArrayList;

import yxy.apple.http.HTTPException;
import yxy.apple.util.ByteUtility;
import yxy.apple.util.IDGenerator;
import yxy.apple.util.IDGenerator.LetterCase;
import yxy.apple.util.http.HTTPProtocol;
import yxy.apple.util.http.HTTPProtocol.MimeType;

public class LargeMultiPartFormDataRequest extends PostRequest<byte[]> {

	private static abstract class ContentPart {
		private String name = null;
		private final String description = "form-data";

		public ContentPart(String name) {
			this.name = name;
		}

		public String name() {
			return this.name;
		}

		public String description() {
			return this.description;
		}

		public byte[] build() {
			StringBuffer buffer = new StringBuffer();
			buffer.append("Content-Disposition");
			buffer.append(": ");
			buffer.append(this.description());

			buffer.append("; ");
			buffer.append("name");
			buffer.append("=");
			buffer.append("\"");
			buffer.append(this.name());
			buffer.append("\"");
			return buffer.toString().getBytes();
		}
	}

	private static class TextPart extends ContentPart {
		private String value = null;

		public TextPart(String name, String value) {
			super(name);
			this.value = value;
		}

		public String value() {
			return this.value;
		}

		public byte[] build() {
			StringBuffer buffer = new StringBuffer();
			buffer.append("\r\n\r\n");
			buffer.append(this.value());
			return ByteUtility.join(super.build(), buffer.toString().getBytes());
		}
	}

	private static class BinaryPart extends ContentPart {
		private byte[] value = null;
		private String filename = null;
		private String type = HTTPProtocol.MimeType.OctetStream;

		public BinaryPart(byte[] value, String filename) {
			super("filename");
			this.value = value;
			this.filename = filename;
		}

		public BinaryPart(byte[] value, String filename, String type) {
			this(value, filename);
			this.type = type;
		}

		public byte[] value() {
			return this.value;
		}

		public String filename() {
			return this.filename;
		}

		public String type() {
			return this.type;
		}

		@Override
		public byte[] build() {
			StringBuffer buffer = new StringBuffer();
			buffer.append("; ");
			buffer.append("filename");
			buffer.append("=");
			buffer.append("\"");
			buffer.append(this.filename());
			buffer.append("\"");

			buffer.append("\r\n");
			buffer.append("Content-Type");
			buffer.append(": ");
			buffer.append(this.type());
			buffer.append("\r\n\r\n");
			byte[] tmp = ByteUtility.join(super.build(), buffer.toString().getBytes());
			return ByteUtility.join(tmp, this.value());
		}
	}

	private ArrayList<ContentPart> parts = new ArrayList<>();

	private String boundary = null;

	public LargeMultiPartFormDataRequest(String url) {
		this(url, IDGenerator.nextLetterSequence(16, LetterCase.RandomCase));
	}

	public LargeMultiPartFormDataRequest(String url, String boundary) {
		super(url);
		this.boundary = boundary;
		String type = HTTPProtocol.MimeType.MultipartFormData;
		type += "; boundary=";
		type += boundary;
		this.setHeader(HTTPProtocol.HeaderKey.ContentType, type);
	}

	@Deprecated
	@Override
	public void setBody(byte[] body) {
		super.setBody(body);
	}

	public void addTextPart(String field, String value) {
		TextPart part = new TextPart(field, value);
		this.parts.add(part);
	}

	public void addBinaryPart(byte[] value, String filename) {
		this.addBinaryPart(value, filename, MimeType.OctetStream);
	}

	public void addBinaryPart(byte[] value, String filename, String type) {
		BinaryPart part = new BinaryPart(value, filename, type);
		this.parts.add(part);
	}

	@Override
	protected String bodyToString(byte[] body) {
		try {
			return new String(this.encodedBody());
		} catch (HTTPException e) {
			return e.getLocalizedMessage();
		}
	}

	@Override
	public byte[] encodedBody() throws HTTPException {
		try {
			//最大内容体长度1M
			ByteBuffer buffer = ByteBuffer.allocate(1024 * 1024);
			for (ContentPart part : parts) {
				StringBuffer title = new StringBuffer();
				title.append("--");
				title.append(this.boundary);
				title.append("\r\n");
				buffer.put(title.toString().getBytes());
				byte[] data = part.build();
				buffer.put(data);
				buffer.put("\r\n".getBytes());
			}
			{
				StringBuffer tail = new StringBuffer();
				tail.append("--");
				tail.append(this.boundary);
				tail.append("--");
				tail.append("\r\n");
				buffer.put(tail.toString().getBytes());
			}
			buffer.flip();
			byte[] result = new byte[buffer.limit()];
			buffer.get(result);
			buffer.clear();
			return result;
		} catch (Exception e) {
			throw new HTTPException(e);
		}
	}

}
